Padziļināts ieskats JavaScript WeakRef un FinalizationRegistry izmantošanā atmiņas efektīvas novērotāja shēmas izveidei. Uzziniet, kā novērst atmiņas noplūdes liela mēroga lietojumprogrammās.
JavaScript WeakRef novērotāja shēma: Atmiņu apzinīgu notikumu sistēmu veidošana
Mūsdienu tīmekļa izstrādes pasaulē vienlapas lietojumprogrammas (SPA) ir kļuvušas par standartu dinamisku un atsaucīgu lietotāja pieredzes radīšanai. Šīs lietojumprogrammas bieži darbojas ilgstoši, pārvaldot sarežģītu stāvokli un apstrādājot neskaitāmas lietotāja mijiedarbības. Tomēr šai ilgmūžībai ir slēptas izmaksas: palielināts atmiņas noplūžu risks. Atmiņas noplūde, kad lietojumprogramma saglabā atmiņu, kas tai vairs nav vajadzīga, laika gaitā var pasliktināt veiktspēju, izraisot lēnumu, pārlūkprogrammas avārijas un sliktu lietotāja pieredzi. Viens no visbiežāk sastopamajiem šo noplūžu avotiem ir fundamentālā dizaina shēma: novērotāja shēma.
Novērotāja shēma ir notikumu vadītas arhitektūras stūrakmens, kas ļauj objektiem (novērotājiem) abonēt un saņemt atjauninājumus no centrālā objekta (subjekta). Tā ir eleganta, vienkārša un neticami noderīga. Taču tās klasiskajai implementācijai ir kritisks trūkums: subjekts uztur spēcīgas atsauces uz saviem novērotājiem. Ja novērotājs pārējai lietojumprogrammai vairs nav vajadzīgs, bet izstrādātājs aizmirst to tieši atabonēt no subjekta, tas nekad netiks savākts ar atkritumu savācēju. Tas paliek iesprostots atmiņā, spoks, kas vajā jūsu lietojumprogrammas veiktspēju.
Šeit moderna JavaScript ar tās ECMAScript 2021 (ES12) funkcijām nodrošina spēcīgu risinājumu. Izmantojot WeakRef un FinalizationRegistry, mēs varam izveidot atmiņu apzinīgu novērotāja shēmu, kas automātiski veic tīrīšanu, novēršot šīs bieži sastopamās noplūdes. Šis raksts ir padziļināts ieskats šajā progresīvajā tehnikā. Mēs izpētīsim problēmu, sapratīsim rīkus, izveidosim robustu implementāciju no nulles un apspriedīsim, kad un kur šo jaudīgo shēmu vajadzētu pielietot jūsu globālajās lietojumprogrammās.
Problēmas būtības izpratne: Klasiskā novērotāja shēma un tās atmiņas patēriņš
Pirms mēs varam novērtēt risinājumu, mums pilnībā jāsaprot problēma. Novērotāja shēma, kas pazīstama arī kā Publicētāja-Abonentā shēma, ir paredzēta komponentu atsaistīšanai. Subjekts (vai Publicētājs) uztur sarakstu ar saviem atkarīgajiem, ko sauc par Novērotājiem (vai Abonentiem). Kad Subjekta stāvoklis mainās, tas automātiski paziņo visiem saviem Novērotājiem, parasti, izsaucot konkrētu metodi uz tiem, piemēram, update().
Apskatīsim vienkāršu, klasisku implementāciju JavaScript.
Vienkārša subjekta implementācija
Šeit ir pamata Subjekta klase. Tai ir metodes, lai abonētu, atabonētu un paziņotu novērotājiem.
class ClassicSubject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
console.log(`${observer.name} ir abonējis.`);
}
unsubscribe(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
console.log(`${observer.name} ir atabonējis.`);
}
notify(data) {
console.log('Paziņo novērotājiem...');
this.observers.forEach(observer => observer.update(data));
}
}
Un šeit ir vienkārša Novērotāja klase, kas var abonēt subjektu.
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} saņēma datus: ${data}`);
}
}
Slēptās briesmas: Atlikušās atsauces
Šī implementācija darbojas perfekti, kamēr mēs apzinīgi pārvaldām mūsu novērotāju dzīves ciklu. Problēma rodas, ja mēs to nedarām. Apsveriet bieži sastopamu scenāriju lielā lietojumprogrammā: ilgstoši pastāvoša globāla datu krātuve (subjekts) un pagaidu lietotāja saskarnes komponents (novērotājs), kas attēlo daļu no šiem datiem.
Simulēsim šo scenāriju:
const dataStore = new ClassicSubject();
function manageUIComponent() {
let chartComponent = new Observer('ChartComponent');
dataStore.subscribe(chartComponent);
// Komponents veic savu darbu...
// Tagad lietotājs pārvietojas prom, un komponents vairs nav vajadzīgs.
// Izstrādātājs var aizmirst pievienot tīrīšanas kodu:
// dataStore.unsubscribe(chartComponent);
chartComponent = null; // Mēs atbrīvojam mūsu atsauci uz komponentu.
}
manageUIComponent();
// Vēlāk lietojumprogrammas dzīves cikla laikā...
dataStore.notify('Jauni dati pieejami!');
Funkcijā `manageUIComponent` mēs izveidojam `chartComponent` un abonējam to mūsu `dataStore`. Vēlāk mēs iestatām `chartComponent` uz `null`, signalizējot, ka esam ar to pabeiguši. Mēs sagaidām, ka JavaScript atkritumu savācējs (GC) redzēs, ka uz šo objektu vairs nav atsauču, un atgūs tā atmiņu.
Bet tur ir cita atsauce! Masīvs `dataStore.observers` joprojām satur tiešu, spēcīgu atsauci uz `chartComponent` objektu. Šīs vienīgās atlikušās atsauces dēļ atkritumu savācējs nevar atgūt atmiņu. `chartComponent` objekts un visi resursi, ko tas satur, paliks atmiņā visu `dataStore` dzīves ciklu. Ja tas notiek atkārtoti — piemēram, katru reizi, kad lietotājs atver un aizver modālo logu — lietojumprogrammas atmiņas patēriņš neierobežoti pieaugs. Šī ir klasiska atmiņas noplūde.
Jauna cerība: WeakRef un FinalizationRegistry ieviešana
ECMAScript 2021 ieviesa divas jaunas funkcijas, kas īpaši paredzētas šāda veida atmiņas pārvaldības izaicinājumu risināšanai: `WeakRef` un `FinalizationRegistry`. Tie ir progresīvi rīki, un tie jāizmanto uzmanīgi, taču mūsu novērotāja shēmas problēmai tie ir ideāls risinājums.
Kas ir WeakRef?
`WeakRef` objekts satur vāju atsauci uz citu objektu, ko sauc par tā mērķi. Galvenā atšķirība starp vāju atsauci un parastu (spēcīgu) atsauci ir šāda: vāja atsauce nenovērš mērķa objekta savākšanu ar atkritumu savācēju.
Ja vienīgās atsauces uz objektu ir vājas atsauces, JavaScript dzinējs drīkst iznīcināt objektu un atgūt tā atmiņu. Tas ir tieši tas, kas mums nepieciešams, lai atrisinātu mūsu novērotāja problēmu.
Lai izmantotu `WeakRef`, jūs izveidojat tā instanci, nododot mērķa objektu konstruktoram. Lai vēlāk piekļūtu mērķa objektam, izmantojat metodi `deref()`.
let targetObject = { id: 42 };
const weakRefToObject = new WeakRef(targetObject);
// Lai piekļūtu objektam:
const retrievedObject = weakRefToObject.deref();
if (retrievedObject) {
console.log(`Objekts joprojām ir dzīvs: ${retrievedObject.id}`); // Izvade: Objekts joprojām ir dzīvs: 42
} else {
console.log('Objekts ir savākts ar atkritumu savācēju.');
}
Svarīgākā daļa ir tā, ka `deref()` var atgriezt `undefined`. Tas notiek, ja `targetObject` ir savākts ar atkritumu savācēju, jo uz to vairs nav spēcīgu atsauču. Šī uzvedība ir mūsu atmiņu apzinīgās novērotāja shēmas pamats.
Kas ir FinalizationRegistry?
Lai gan `WeakRef` ļauj savākt objektu, tas nedod mums skaidru veidu, kā uzzināt, kad tas ir savākts. Mēs varētu periodiski pārbaudīt `deref()` un noņemt `undefined` rezultātus no mūsu novērotāju saraksta, taču tas ir neefektīvi. Šeit nāk klāt `FinalizationRegistry`.
`FinalizationRegistry` ļauj reģistrēt atzvanu funkciju, kas tiks izsaukta pēc reģistrētā objekta savākšanas ar atkritumu savācēju. Tas ir mehānisms pēcnāves tīrīšanai.
Lūk, kā tas darbojas:
- Jūs izveidojat reģistru ar tīrīšanas atzvanu.
- Jūs `register()` objektu ar reģistru. Jūs varat arī norādīt `heldValue`, kas ir datu fragments, kas tiks nodots jūsu atzvanam, kad objekts tiks savākts. Šim `heldValue` nedrīkst būt tieša atsauce uz pašu objektu, jo tas atceltu mērķi!
// 1. Izveidojiet reģistru ar tīrīšanas atzvanu
const registry = new FinalizationRegistry(heldValue => {
console.log(`Objekts ir savākts ar atkritumu savācēju. Tīrīšanas marķieris: ${heldValue}`);
});
(function() {
let objectToTrack = { name: 'Pagaidu dati' };
let cleanupToken = 'temp-data-123';
// 2. Reģistrējiet objektu un nodrošiniet marķieri tīrīšanai
registry.register(objectToTrack, cleanupToken);
// objectToTrack iziet no darbības lauka šeit
})();
// Kādā brīdī nākotnē, pēc GC palaišanas, konsolē tiks ierakstīts:
// "Objekts ir savākts ar atkritumu savācēju. Tīrīšanas marķieris: temp-data-123"
Svarīgi brīdinājumi un labākā prakse
Pirms mēs iedziļināmies implementācijā, ir kritiski svarīgi saprast šo rīku būtību. Atkritumu savācēja uzvedība ir ļoti atkarīga no implementācijas un ir nedeterminēta. Tas nozīmē:
- Jūs nevarat paredzēt, kad objekts tiks savākts. Tas var būt sekundes, minūtes vai pat ilgāk pēc tam, kad tas kļūst nesasniedzams.
- Jūs nevarat paļauties uz `FinalizationRegistry` atzvaniem, ka tie darbosies savlaicīgi vai paredzamā veidā. Tie ir paredzēti tīrīšanai, nevis kritiskai lietojumprogrammas loģikai.
- `WeakRef` un `FinalizationRegistry` pārmērīga izmantošana var apgrūtināt koda izpratni. Vienmēr dodiet priekšroku vienkāršākiem risinājumiem (piemēram, tiešiem `unsubscribe` izsaukumiem), ja objekta dzīves cikli ir skaidri un pārvaldāmi.
Šīs funkcijas vislabāk ir piemērotas situācijām, kad viena objekta (novērotāja) dzīves cikls ir patiesi neatkarīgs no cita objekta (subjekta) un tam nezināms.
`WeakRefObserver` shēmas izveide: Soli pa solim implementācija
Tagad apvienosim `WeakRef` un `FinalizationRegistry`, lai izveidotu atmiņu drošu `WeakRefSubject` klasi.
1. solis: `WeakRefSubject` klases struktūra
Mūsu jaunā klase glabās `WeakRef` uz novērotājiem, nevis tiešas atsauces. Tai būs arī `FinalizationRegistry`, lai apstrādātu novērotāju saraksta automātisko tīrīšanu.
class WeakRefSubject {
constructor() {
this.observers = new Set(); // Izmanto Set vieglākai noņemšanai
// Finalizētāja atzvans. Tas saņem noturēto vērtību, ko mēs nodrošinām reģistrācijas laikā.
// Mūsu gadījumā noturētā vērtība būs pati WeakRef instance.
this.cleanupRegistry = new FinalizationRegistry(weakRefObserver => {
console.log('Finalizētājs: Novērotājs ir savākts ar atkritumu savācēju. Tiek veikta tīrīšana...');
this.observers.delete(weakRefObserver);
});
}
}
Mēs izmantojam `Set`, nevis `Array` savam novērotāju sarakstam. Tas ir tāpēc, ka vienuma dzēšana no `Set` ir daudz efektīvāka (vidēji O(1) laika sarežģītība) nekā `Array` filtrēšana (O(n)), kas būs noderīga mūsu tīrīšanas loģikā.
2. solis: `subscribe` metode
Metode `subscribe` ir vieta, kur sākas maģija. Kad novērotājs abonē, mēs:
- Izveidosim `WeakRef`, kas norāda uz novērotāju.
- Pievienosim šo `WeakRef` mūsu `observers` kopai.
- Reģistrēsim oriģinālo novērotāja objektu mūsu `FinalizationRegistry`, izmantojot jaunizveidoto `WeakRef` kā `heldValue`.
// Iekš WeakRefSubject klases...
subscribe(observer) {
// Pārbaudiet, vai novērotājs ar šo atsauci jau pastāv
for (const ref of this.observers) {
if (ref.deref() === observer) {
console.warn('Novērotājs jau ir abonēts.');
return;
}
}
const weakRefObserver = new WeakRef(observer);
this.observers.add(weakRefObserver);
// Reģistrējiet oriģinālo novērotāja objektu. Kad tas tiks savākts,
// finalizētājs tiks izsaukts ar `weakRefObserver` kā argumentu.
this.cleanupRegistry.register(observer, weakRefObserver);
console.log('Novērotājs ir abonējis.');
}
Šis iestatījums rada gudru cilpu: subjekts uztur vāju atsauci uz novērotāju. Reģistrs uztur spēcīgu atsauci uz novērotāju (iekšēji), līdz tas tiek savākts ar atkritumu savācēju. Pēc savākšanas reģistra atzvans tiek aktivizēts ar vājās atsauces instanci, ko mēs pēc tam varam izmantot, lai sakārtotu mūsu `observers` kopu.
3. solis: `unsubscribe` metode
Pat ar automātisko tīrīšanu mums joprojām jāsniedz manuāla `unsubscribe` metode gadījumiem, kad ir nepieciešama deterministiska noņemšana. Šai metodei būs jāatrod pareizā `WeakRef` mūsu kopā, dereferencējot katru no tiem un salīdzinot ar novērotāju, kuru vēlamies noņemt.
// Iekš WeakRefSubject klases...
unsubscribe(observer) {
let refToRemove = null;
for (const weakRef of this.observers) {
if (weakRef.deref() === observer) {
refToRemove = weakRef;
break;
}
}
if (refToRemove) {
this.observers.delete(refToRemove);
// SVARĪGI: Mums ir arī jāatceļ reģistrācija no finalizētāja,
// lai novērstu atzvana nevajadzīgu palaišanu vēlāk.
this.cleanupRegistry.unregister(observer);
console.log('Novērotājs ir manuāli atabonējis.');
}
}
4. solis: `notify` metode
Metode `notify` iterē pa mūsu `WeakRef` kopu. Katrai no tām tā mēģina veikt `deref()`, lai iegūtu faktisko novērotāja objektu. Ja `deref()` izdodas, tas nozīmē, ka novērotājs joprojām ir dzīvs, un mēs varam izsaukt tā `update` metodi. Ja tas atgriež `undefined`, novērotājs ir savākts, un mēs to vienkārši varam ignorēt. `FinalizationRegistry` galu galā noņems tā `WeakRef` no kopas.
// Iekš WeakRefSubject klases...
notify(data) {
console.log('Paziņo novērotājiem...');
for (const weakRefObserver of this.observers) {
const observer = weakRefObserver.deref();
if (observer) {
// Novērotājs joprojām ir dzīvs
observer.update(data);
} else {
// Novērotājs ir savākts ar atkritumu savācēju.
// FinalizationRegistry apstrādās šīs weakRef noņemšanu no kopas.
console.log('Notifikācijas laikā atrasta miruša novērotāja atsauce.');
}
}
}
Visu kopā: Praktisks piemērs
Atgriezīsimies pie mūsu UI komponenta scenārija, bet šoreiz izmantojot mūsu jauno `WeakRefSubject`. Vienkāršības labad izmantosim to pašu `Observer` klasi kā iepriekš.
// Tā pati vienkāršā Observer klase
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} saņēma datus: ${data}`);
}
}
Tagad izveidosim globālu datu pakalpojumu un simulēsim pagaidu UI logrīku.
const globalDataService = new WeakRefSubject();
function createAndDestroyWidget() {
console.log('--- Izveido un abonē jaunu logrīku ---');
let chartWidget = new Observer('RealTimeChartWidget');
globalDataService.subscribe(chartWidget);
// Logrīks tagad ir aktīvs un saņems paziņojumus
globalDataService.notify({ price: 100 });
console.log('--- Iznīcina logrīku (atbrīvo mūsu atsauci) ---');
// Mēs esam pabeiguši ar logrīku. Mēs iestatām mūsu atsauci uz null.
// Mums NAV jāizsauc unsubscribe().
chartWidget = null;
}
createAndDestroyWidget();
console.log('--- Pēc logrīka iznīcināšanas, pirms atkritumu savākšanas ---');
globalDataService.notify({ price: 105 });
Pēc `createAndDestroyWidget()` palaišanas objekts `chartWidget` tagad tiek atsaucts tikai ar `WeakRef` mūsu `globalDataService` iekšienē. Tā kā šī ir vāja atsauce, objekts tagad ir piemērots atkritumu savākšanai.
Kad atkritumu savācējs galu galā palaidīsies (ko mēs nevaram paredzēt), notiks divas lietas:
- `chartWidget` objekts tiks noņemts no atmiņas.
- Mūsu `FinalizationRegistry` atzvans tiks aktivizēts, kas pēc tam noņems tagad mirušo `WeakRef` no `globalDataService.observers` kopas.
Ja mēs vēlreiz izsaucam `notify` pēc atkritumu savācēja palaišanas, `deref()` izsaukums atgriezīs `undefined`, mirušais novērotājs tiks izlaists, un lietojumprogramma turpinās darboties efektīvi bez atmiņas noplūdēm. Mēs esam veiksmīgi atsaistījuši novērotāja dzīves ciklu no subjekta.
Kad izmantot (un kad izvairīties) no `WeakRefObserver` shēmas
Šī shēma ir jaudīga, taču tā nav sudraba lode. Tā ievieš sarežģītību un paļaujas uz nedeterministisku uzvedību. Ir svarīgi zināt, kad tas ir pareizais rīks darbam.
Ideāli lietošanas gadījumi
- Ilgstoši subjekti un īslaicīgi novērotāji: Tas ir kanoniskais lietošanas gadījums. Globāls pakalpojums, datu krātuve vai kešatmiņa (subjekts), kas pastāv visu lietojumprogrammas dzīves ciklu, kamēr daudzi lietotāja saskarnes komponenti, pagaidu darbinieki vai spraudņi (novērotāji) tiek bieži izveidoti un iznīcināti.
- Kešatmiņas mehānismi: Iedomājieties kešatmiņu, kas sarežģītu objektu kartē ar kādu aprēķinātu rezultātu. Jūs varat izmantot `WeakRef` galvenajam objektam. Ja oriģinālais objekts tiek savākts ar atkritumu savācēju no pārējās lietojumprogrammas, `FinalizationRegistry` var automātiski sakārtot atbilstošo ierakstu jūsu kešatmiņā, novēršot atmiņas uzpūšanos.
- Spraudņu un paplašinājumu arhitektūras: Ja veidojat pamatsistēmu, kas ļauj trešo pušu moduļiem abonēt notikumus, `WeakRefObserver` izmantošana pievieno noturības slāni. Tas novērš slikti uzrakstīta spraudņa, kas aizmirst atabonēt, izraisīšanu atmiņas noplūdi jūsu pamata lietojumprogrammā.
- Datu kartēšana uz DOM elementiem: Scenārijos bez deklaratīvas ietvara jūs varētu vēlēties saistīt dažus datus ar DOM elementu. Ja to glabājat kartē ar DOM elementu kā atslēgu, varat radīt atmiņas noplūdi, ja elements tiek noņemts no DOM, bet joprojām atrodas jūsu kartē. `WeakMap` šeit ir labāka izvēle, taču princips ir tas pats: datu dzīves ciklam jābūt saistītam ar elementa dzīves ciklu, nevis otrādi.
Kad pieturēties pie klasiskā novērotāja
- Cieši saistīti dzīves cikli: Ja subjekts un tā novērotāji vienmēr tiek izveidoti un iznīcināti kopā vai vienā un tajā pašā darbības jomā, `WeakRef` izmantošanas izmaksas un sarežģītība ir nevajadzīga. Vienkāršs, tiešs `unsubscribe()` izsaukums ir lasāmāks un paredzamāks.
- Veiktspējas kritiski karstie ceļi: Metodei `deref()` ir nelielas, bet ne nulles veiktspējas izmaksas. Ja jūs paziņojat tūkstošiem novērotāju simtiem reižu sekundē (piemēram, spēles cilpā vai augstas frekvences datu vizualizācijā), klasiskā implementācija ar tiešām atsaucēm būs ātrāka.
- Vienkāršas lietojumprogrammas un skripti: Mazākām lietojumprogrammām vai skriptiem, kur lietojumprogrammas darbības laiks ir īss un atmiņas pārvaldība nav nozīmīga problēma, klasiskā shēma ir vienkāršāk implementējama un saprotama. Nepievienojiet sarežģītību, kur tā nav nepieciešama.
- Kad ir nepieciešama deterministiska tīrīšana: Ja jums ir jāveic darbība tieši tajā brīdī, kad novērotājs tiek atsaistīts (piemēram, atjaunināt skaitītāju, atbrīvot konkrētu aparatūras resursu), jums obligāti jāizmanto manuāla `unsubscribe()` metode. `FinalizationRegistry` nedeterministiskā daba padara to nepiemērotu loģikai, kurai ir jāizpildās paredzami.
Plašākas programmatūras arhitektūras sekas
Vājo atsauču ieviešana augsta līmeņa valodā, piemēram, JavaScript, liecina par platformas nobriešanu. Tā ļauj izstrādātājiem veidot sarežģītākas un noturīgākas sistēmas, īpaši ilgstoši darbojošām lietojumprogrammām. Šī shēma veicina arhitektūras domāšanas maiņu:
- Patiesa atsaiste: Tā nodrošina atsaistes līmeni, kas pārsniedz tikai saskarni. Mēs tagad varam atsaistīt komponentu dzīves ciklus. Subjektam vairs nav jāzina nekas par to, kad tiek izveidoti vai iznīcināti tā novērotāji.
- Noturība pēc dizaina: Tā palīdz veidot sistēmas, kas ir noturīgākas pret programmētāja kļūdām. Aizmirsts `unsubscribe()` izsaukums ir bieži sastopama kļūda, ko var būt grūti atrast. Šī shēma mazina visu šo kļūdu klasi.
- Ietvaru un bibliotēku autoru iespējošana: Tiem, kas veido ietvarus, bibliotēkas vai platformas citiem izstrādātājiem, šie rīki ir nenovērtējami. Tie ļauj izveidot robustas API, kas ir mazāk pakļautas bibliotēkas patērētāju nepareizai lietošanai, tādējādi nodrošinot stabilākas lietojumprogrammas kopumā.
Secinājums: Spēcīgs rīks mūsdienu JavaScript izstrādātājam
Klasiskā novērotāja shēma ir programmatūras dizaina pamats, taču tās paļaušanās uz spēcīgām atsaucēm jau sen ir bijusi smalku un nomācošu atmiņas noplūžu avots JavaScript lietojumprogrammās. Līdz ar `WeakRef` un `FinalizationRegistry` parādīšanos ES2021, mums tagad ir rīki, lai pārvarētu šo ierobežojumu.
Mēs esam ceļojuši no pamatproblēmas, kas saistīta ar atlikušajām atsaucēm, izpratnes līdz pilnīgas, atmiņu apzinīgas `WeakRefSubject` izveidei no nulles. Mēs esam redzējuši, kā `WeakRef` ļauj objektus savākt ar atkritumu savācēju pat tad, ja tie tiek 'novēroti', un kā `FinalizationRegistry` nodrošina automatizētu tīrīšanas mehānismu, lai saglabātu mūsu novērotāju sarakstu nevainojamu.
Tomēr ar lielu spēku nāk liela atbildība. Šīs ir progresīvas funkcijas, kuru nedeterministiskā daba prasa rūpīgu apsvēršanu. Tās nav laba lietojumprogrammas dizaina un apzinīgas dzīves cikla pārvaldības aizstājējs. Bet, ja tās tiek pielietotas pareizajām problēmām – piemēram, komunikācijas pārvaldīšanai starp ilgstoši darbojošiem pakalpojumiem un īslaicīgiem komponentiem – WeakRef novērotāja shēma ir ārkārtīgi jaudīga tehnika. To apgūstot, jūs varat rakstīt robustākas, efektīvākas un mērogojamākas JavaScript lietojumprogrammas, kas ir gatavas apmierināt mūsdienu dinamiskā tīmekļa prasības.